Maps are one of the most common and useful data visualisation tools in an ecologist’s tool belt. Making a quick and simple map of species observations is especially useful when first investigating where a species has occurred. Viewing locations of points helps to understand the extent of your data (and spot possible errors or outliers).
In this post, we will use Python to make a map in under 5 minutes of Peron’s tree frog (Litoria peronii) observations in New South Wales, Australia recorded by FrogID since 2018 using the galah_python, geopandas and matplotlib packages.
Download data
Peron’s Tree frog is one of the most recorded frog species in the Atlas of Living Australia. Growing up to 7cm in length, it is well-known for its eyes which often look like they have a black cross on them!
Left: Litoria peronii (Giverny CC-BY-NC 4.0 (Int), Middle: Litoria peronii (debtaylor142 CC-BY-NC 4.0 (Int)), Right: Litoria peronii (Ernst Weiher, iNaturalist CC-BY-NC 4.0 (Int))
First, let’s import galah-python.
import galahTo run a command asking galah-python to tell you the total number of records the Atlas of Living Australia has, use atlas_counts.
galah.atlas_counts() totalRecords
0 132243624
We can then narrow down the number of record counts by providing one or more of the following:
- Species scientific name(s)
- Filters
To find species’ scientific names, we suggest using the search_taxa() function of galah-python or by doing a Google search of your favourite species. To find other ways to narrow your query, you can use the galah.show_all() command, which will show you all possible fields to narrow search. However, if you want to search for a particular string of text in a field, use the galah.search_all() function.
import pandas as pd
galah.search_all(fields="year") id description type link
0 year The year in which an occurrence was observed. ... field https://github.com/AtlasOfLivingAustralia/ala-...
1 endDayOfYear http://rs.tdwg.org/dwc/terms/endDayOfYear field NaN
2 datePrecision The precision of the date information for the ... field NaN
3 occurrenceYear Year ranges for a search. Calculated based on ... field NaN
4 startDayOfYear http://rs.tdwg.org/dwc/terms/startDayOfYear field NaN
5 namePublishedInYear http://rs.tdwg.org/dwc/terms/namePublishedInYear field NaN
If we were to choose year as our filter, the other things we need to do is to find what value we want to filter by. To find all possible values of year, use the show_values() function. To search for specific values, use the search_values() function.
Let’s map the locations of Peron’s tree frog since 2018 in New South Wales by FrogID. First, we will find how many records there are of Peron’s tree frog that match our query. It’s good practice check how many observations there are of a given species so you know how many to expect when you download them!
We’ll use atlas_counts() to download record counts, specifying the taxon we want using the taxa argument, and narrowing the year range, state/territory and data resource using the filters argument.
galah.atlas_counts( # *Download record counts*
taxa="litoria peronii", # *of Peron's tree frog*
filters=["year>=2018", # *since 2018*
"cl22=New South Wales", # *in New South Wales*
"dataResourceName=FrogID"] # *by FrogID*
) totalRecords
0 27647
Now we can use atlas_occurrences() to download occurrence records!
You will need to first provide a registered email with the ALA using galah_config() before retrieving records.
galah.galah_config(email = "your-email-here")
frogs = galah.atlas_occurrences(
taxa="litoria peronii",
filters=["year>=2018",
"cl22=New South Wales",
"dataResourceName=FrogID"]
)
frogs decimalLatitude decimalLongitude eventDate scientificName taxonConceptID recordID dataResourceName occurrenceStatus
0 -37.246800 149.375000 2020-12-27T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... a5cd2fcd-5225-4d19-977c-b16ca5e8f1dd FrogID PRESENT
1 -37.089036 149.699526 2020-12-14T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... eebde5ef-cac4-4897-af00-cb2e39a0684f FrogID PRESENT
2 -37.077693 149.874402 2018-01-06T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... 35340478-97c1-48a4-a463-991fe3a8daa0 FrogID PRESENT
3 -37.077241 149.874787 2018-01-06T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... a7abc9f3-362f-469e-9076-5b55a2447b69 FrogID PRESENT
4 -37.070746 149.896011 2020-12-13T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... 1cc9dda8-f2d4-4f55-acf6-11c93b26da9e FrogID PRESENT
... ... ... ... ... ... ... ... ...
27642 -28.207514 153.442592 2018-11-15T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... b094fed1-5bff-4df8-b556-cabd693c533a FrogID PRESENT
27643 -28.207472 153.442497 2018-11-15T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... 5cc24fbd-8c6b-4a76-9b28-fec76ee08f37 FrogID PRESENT
27644 -28.207442 153.442328 2020-02-07T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... 61aa50a1-4c79-4fc3-b3ab-93538faa37b1 FrogID PRESENT
27645 -28.207108 153.443021 2021-02-19T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... 0324b8d1-77b0-4bf3-9ec2-6ad9efff18f2 FrogID PRESENT
27646 -28.186157 153.445556 2018-11-16T00:00:00Z Litoria peronii https://biodiversity.org.au/afd/taxa/c584f24b-... bcf83a54-a900-4265-960a-9436356a7107 FrogID PRESENT
[27647 rows x 8 columns]
As we can see, we get 8 columns by default from the ALA; to make our map,however, we only need scientificName,decimalLatitude, and decimalLongitude columns.
Make a map
It’s time to make our map!
In order to draw our map of New South Wales, we’ll download a shapefile of the latest state and territory boundaries from the Australian Bureau of statistics (link here). Download the “States and Territories - 2021 - Shapefile” zip file, and save the zip file in the same folder you are coding in.
Let’s load our States and Territories shapefile with read_file() and save it as states. Then, we will choose the part of the shape that represents New South Wales. We will also specify that the edges of the shape are black, the inside of the shape is white, and the figure size is in inches, the default unit for matplotlib.
from matplotlib import pyplot as plt
import geopandas as gpd
# Load Australian state and territory boundaries
states = gpd.read_file("STE_2021_AUST_GDA94.shp")
# Filter to New South Wales and plot
states[states["STE_NAME21"] == "New South Wales"].plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))We will also set the Coordinate References System (CRS), which determines how our points on the spherical globe are oriented when drawn as a flat surface. The projection of ALA data is EPSG:4326 (also known as “WGS84”). Setting the CRS allows us to make sure the points of our data align correctly with our shapefile.
states = states.to_crs(4326)Now, we will add our species data to the map. First, we will plot the shapefile as shown in the codeblock above. Then, we will create a scatter plot using decimalLongitude as your x axis, and decimalLatitude as your y axis. c represents the colour the scatterplot is, and alpha is how transparent the dots are (1 is no transparency, 0 is fully transparent).
states[states["STE_NAME21"] == "New South Wales"].plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))
plt.scatter(frogs['decimalLongitude'],frogs['decimalLatitude'], c = "#6fab3f", alpha = 0.5)For some final touches (to make the map prettier), we can add labels, titles and legends, as well as save the figure.
states[states["STE_NAME21"] == "New South Wales"].plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))
plt.scatter(frogs['decimalLongitude'],frogs['decimalLatitude'], c = "#6fab3f", alpha = 0.5, label = "Litoria peronii")
plt.legend()
plt.xlabel("Longitude",fontsize=16)
plt.ylabel("Latitude",fontsize=16)
plt.title("Peron's tree frog\nFrogID observations in New South Wales since 2018",fontsize=20)To save your plot in your current folder, you can use:
plt.savefig("perons_tree_frog_nsw.png")Code
import galah
from matplotlib import pyplot as plt
import geopandas as gpd
# Load Australian state and territory boundaries
states = gpd.read_file("STE_2021_AUST_GDA94.shp")
# Filter to New South Wales
states[states["STE_NAME21"] == "New South Wales"].plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))
# Change your Coordinate Reference System to the same as the ALA CRS
states = states.to_crs(4326)
# Plot your shape file
ax = states[states["STE_NAME21"] == "New South Wales"].plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))
# Plot your species points over the shape file
plt.scatter(frogs['decimalLongitude'],frogs['decimalLatitude'], c = "#6fab3f", alpha = 0.5)
# add a legend to show what species you are plotting
plt.legend()
# set X and Y label so users know they are looking at degrees latitude and longitude
ax.set_xlabel("Longitude",fontsize=16)
ax.set_ylabel("Latitude",fontsize=16)
# add a title to your plot
plt.title("Peron's tree frog\nFrogID observations in New South Wales since 2018",fontsize=20)Final thoughts
We hope this post has helped make the basic steps of making a map simple and easy to understand. For more advanced mapping in Python, check out our ALA Labs article on how to map invasive species.
Expand for session info
-----
galah 0.7.0
geopandas 0.13.2
matplotlib 3.7.2
natsort 8.4.0
pandas 2.0.3
session_info 1.0.0
-----
Python 3.9.16 | packaged by conda-forge | (main, Feb 1 2023, 21:40:25) [Clang 14.0.6 ]
macOS-13.5.2-arm64-arm-64bit
-----
Session information updated at 2023-11-29 10:54